<?

//класс для работы с моделями/данными/бизнес-логикой
class Model extends Base {
	
	public $fk=false;
	public $pagination=true;

	//загрузка класса модели и создание его экземпляра 
	public static function Load($name,$db=null) {
		$config = Config::S();
		$vmvc = vmvc::S();
		$path = $config->application->app_dir.'/'.$name;
		//передаваемый в метод параметр $name имеет вид <имяПапки/имяФайлаМодели(без расширения .php)>
		if (file_exists("$path.php")) {
			include_once "$path.php";
			//на основании пути к файлу модели, строится имя класса модели: имяПапки_имяМодели
			$class= str_replace('/','_',$name);
			
			
		} //если файл модели не существует, сообщаем об этом программисту
		else {
			$class = 'Model';
		}
		if($vmvc->$class&&$class!='Model') return $vmvc->$class;
		else {
			$classnms = __NAMESPACE__ . "\\" . $class;
			//создаем экземпляр класса модели
			$vmvc->$class =  $classnms::G();
			//передаем в экземпляр префикс таблиц БД
			$vmvc->$class->_prefix=$config->database->_prefix;
			/*задаем имя таблицы БД по-умолчанию (сегмент в $name до самого первого слэша)
			к примеру, это может быть users, т.е. файл модели находится в папке users, и соотв. таблица по-умолчанию для работы в БД - users
			обычно в одной папке хранятся одноименный контроллер и файл модели*/
			$parts = explode("/",$name);
			$vmvc->$class->table = $vmvc->$class->_prefix.$parts[0];
			//передаем в экземпляр класса модели экземпляр класса для работы с БД
			$vmvc->$class->db = $db?$db:DB::G();
			//возвращаем объект модели
			return $vmvc->$class;
			//else throw new \Exception ('Model file not found: ' . "$path.php");
		}
	}

	function Create($values, $table="") {
			return $this->Add($values, $table="");
	}
	
	
		/*обертка для чтения строки из БД, пареметры: 
		 * $id - какое значение искать
		 * $fieldname - имя поля (в основном primary key), по которому искать
		 * $table - таблица, в которой производится поиск
		 * $fetchpdo - формат вывода данных
		 */
        public function Read($id=null, $table="", $pk=null, $fetchpdo=\PDO::FETCH_CLASS)
        {
			//если параметр $table не передан, используем таблицу по-умолчанию
            $table = $table?$table:$this->table;

			$sql = "SELECT * FROM `{$table}`";

			if(!$pk) {
				$this->pk = $pk = $this->GetPK();
			}
			
			if($this->fk) {
				$this->fk = $fkeys = $this->foreign_key_list();
				foreach($fkeys as $fkey) $sql.=" LEFT JOIN $fkey->table ON ($fkey->table.$fkey->to=$table.$fkey->from)";
			}

			if($id) {
            
				$sql .= " WHERE `$pk`=:id";
				$stmt = $this->db->prepare($sql);
				$stmt->bindParam(':id', $id);
            } else {
				if($this->filtering) {
					$where = " WHERE 1";
					foreach(Request::S()->GET as $g=>$filter) {
						if($filter||is_numeric($filter)) $where .= " AND $table.$g='$filter'";
					}
					$sql.=$where;
				}
				if($this->sorting) {
					$tableSort = Router::S()->getRoute()['controllerName'];
					if(Request::S()->post[$tableSort.'_sort']) Session::S()->{$tableSort.'_sort'}=Request::S()->post[$tableSort.'_sort'];
					elseif(!Session::S()->{$tableSort.'_sort'}) Session::S()->{$tableSort.'_sort'} = "{$tableSort}_id DESC";
					$this->sorting = Session::S()->{$tableSort.'_sort'};
					$sql.=" ORDER BY $this->sorting";
				}
				if($this->pagination) {
					$this->pagination = array('count'=>$this->count($where),'start'=>(int)Uri::S()->fragment(2),'onpage'=>10);
					$sql.=" LIMIT {$this->pagination['start']},{$this->pagination['onpage']}";
					
				}
				$stmt = $this->db->prepare($sql);
			}
            $stmt->execute();
            if(is_object($stmt)) { $result = $stmt->fetchAll($fetchpdo); return $id===null?$result:(isset($result[0])?$result[0]:null); }
            else  throw new \Exception ("Can't read from model!");
        }

        public function count($where="",$table="") {
			$table = $table?$table:$this->table;
			return $this->Query("SELECT COUNT(*) as count FROM $table $where")->fetchObject()->count;
        }

        function table_info($table="") {
			$table = $table?$table:$this->table;
			$rows = $this->ReadSql("PRAGMA table_info($table)");
			foreach($rows as $row) $newrows[$row->name] = $row;
			return $newrows;
        }

        function foreign_key_list($table="") {
			$table = $table?$table:$this->table;
			return $this->ReadSql("PRAGMA foreign_key_list($table)");
        }


		public function GetPK($table="") {
			$table = $table?$table:$this->table;
			switch($this->db->db_type) {
				case "mysql":
					$pk = $this->readSql("SHOW KEYS FROM $table WHERE Key_name = 'PRIMARY'");
					return $pk[0]->Column_name;
				break;
				default:
					$pk = $this->readSql("PRAGMA table_info($table)");
					foreach($pk as $row) if($row->pk) return $row->name;
			}
			
		}
		
        //выполняет исходный запрос на считывание как есть и возвращает данные из БД
        public function ReadSql($sql, $fetchpdo=\PDO::FETCH_CLASS)
        {
           $res =$this->db->query($sql);
           if(is_object($res)) return $res->fetchAll($fetchpdo); 
           else  throw new \Exception ("Can't read from model!");
        }

        //просто выполняет исходный запрос как есть
        public function Query($sql)
        {
            return $this->db->query($sql);
        }


        /**
         *вставляет данные в БД
         * $values - массив, в котором ключ - имя поля в БД, а значение - значение для вставки в это поле
         */
        public function Add($values, $table="")
        {
           $table = $table?$table:$this->table;
            /*** получаем массив ключей-имен полей БД ***/
            $fieldnames = array_keys($values);
            $sql = "INSERT INTO {$table} ";
            /*** задаем имена полей ***/
            $fields = '( `' . implode('`, `', $fieldnames) . '` )';
            /*** задаем плейсхолдеры ***/
            $bound = '(:' . implode(', :', $fieldnames) . ' )';
            /*** объединяем ***/
            $sql .= $fields.' VALUES '.$bound;

            /*** подготавливаем и исполняем ***/
            $this->db->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
            $stmt = $this->db->prepare($sql);
            $stmt->execute($values);
            return $this->db->lastInsertId();
        }

        /**
         *
         * обновляет данные в БД
         * $values - массив (ключи - имена полей в БД, значения - новые значения)
         * $pk - primary key - поле, по которому ищется строка, где нужно произвести изменения
         * $id - идентификатор строки
         *
         */
        public function Edit($values, $id, $table="", $pk=null)
        {
            $table = $table?$table:$this->table;
            $fieldnames = array_keys($values);
            $sql = "UPDATE {$table} SET ";
			$fields="";
            foreach($fieldnames as $field) $fields .= "`$field` = :$field, ";
            if(!$pk) {
				$pk = $this->GetPK();
            }
            $sql .= rtrim($fields,', ')." WHERE `$pk` = :$pk";
            $stmt = $this->db->prepare($sql);
            $values[$pk] = $id;
            return $stmt->execute($values) or die(print_r($stmt->errorInfo(), true));
        }

        public function Update($values, $id, $table="", $pk=null) {
			return $this->Edit($values, $id, $table="", $pk=null);
        }


        /**
         *
         * Удаляет строку из БД
         * $pk - primary key - поле, по которому ищется удаляемая строка
         * $id - идентификатор строки
         */
        public function Del($id, $table="",$pk=null)
        {
            $table = $table?$table:$this->table;
            if(!$pk) {
				$pk = $this->GetPK();
            }
            $sql = "DELETE FROM `{$table}` WHERE `$pk` = :id";
            $stmt = $this->db->prepare($sql);
            $stmt->bindParam(':id', $id, \PDO::PARAM_STR);
            return $stmt->execute() or die(print_r($stmt->errorInfo(), true));
        }

        public function Delete($id, $table="",$pk=null) {
			return $this->Del($id, $table="",$pk=null);
        }
}
?>
